
package com.hlkj.pay.app.merchant.ext.impl.helipay.handler;
/*
 * Hlpay-Plus aggregate payment system.
 * Copyright (c) 2024-2025 Hlpay Team Copyright has the right of final interpretation.
 */

import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import com.hlkj.framework.common.pojo.CommonResult;
import com.hlkj.framework.common.util.id.IdUtils;
import com.hlkj.framework.common.util.json.JsonUtils;
import com.hlkj.pay.app.merchant.ext.dto.access.ChannelMerchantAccessContext;
import com.hlkj.pay.app.merchant.ext.impl.helipay.dto.HeliPayConfig;
import com.hlkj.pay.app.merchant.ext.impl.helipay.dto.HeliPayMccDto;
import com.hlkj.pay.common.MerchantResultCode;
import com.hlkj.pay.dto.merchant.MerchantAccessDto;
import com.hlkj.pay.dto.merchant.MerchantAccessScene;
import com.hlkj.pay.dto.merchant.ProviderChannelFeeConfigList;
import com.hlkj.pay.dto.provider.DefaultFeeConfig;
import com.hlkj.pay.dto.provider.SeniorFeeConfig;
import com.hlkj.pay.dto.provider.StepFeeConfig;
import com.hlkj.pay.enums.CommonEnum;
import com.hlkj.pay.enums.MerchantEnum;
import com.hlkj.pay.enums.PayEnums;
import com.hlkj.pay.infrastructure.model.merchant.*;
import com.hlkj.pay.infrastructure.model.pay.PayChannelDictDO;
import com.hlkj.pay.infrastructure.model.pay.PayChannelRequestDO;
import com.hlkj.pay.infrastructure.model.pay.PayWayDO;

import lombok.extern.slf4j.Slf4j;

/**
 * 汇付 商户进件参数处理
 * 
 * @author HlpayTeam
 * @date 2024/09/26 12:02
 */
@Slf4j
@Service
public class HeliPayMerchantParamsHandle extends HeliPayMerchantCommonParamsHandle {

    /**
     * 企业商户进件-新增参数组装
     *
     * @param channelMerchantAccessContext
     * @return
     */
    public CommonResult<Void> merchantApplyParamsCheck(ChannelMerchantAccessContext channelMerchantAccessContext) {
        MerchantAccessDto merchantAccessDto = channelMerchantAccessContext.getMerchantAccessDto();
        MerchantAccessBusinessDO merchantAccessBusiness = merchantAccessDto.getMerchantAccessBusiness();
        MerchantAccessInfoDO merchantAccessInfo = merchantAccessDto.getMerchantAccessInfo();
        String name = merchantAccessBusiness.getShortName();
        String shortName = merchantAccessInfo.getName();
        MerchantAccessCardDO merchantAccessCard = merchantAccessDto.getMerchantAccessCard();
        // shortName 为注册名称
        // name 为经营名称
        if (!StringUtils.hasText(merchantAccessCard.getBranchCode())) {
            return CommonResult.error(MerchantResultCode.MERCHANT_ACCESS_PARAMS_ERROR.getCode(), MerchantResultCode.MERCHANT_ACCESS_PARAMS_ERROR.getMsg() + "合利宝 联行号必填");
        }
        List<ProviderChannelFeeConfigList> merchantFeeConfigList = channelMerchantAccessContext.getMerchantFeeConfigList();
        for (ProviderChannelFeeConfigList merchantFeeConfig : merchantFeeConfigList) {
            DefaultFeeConfig defaultFeeConfigValue = merchantFeeConfig.getDefaultFeeConfigValue();
            PayWayDO payWayDO = merchantFeeConfig.getPayWayDO();
            if (defaultFeeConfigValue == null) {
                return CommonResult.error(MerchantResultCode.MERCHANT_ACCESS_PARAMS_ERROR.getCode(),
                        MerchantResultCode.MERCHANT_ACCESS_PARAMS_ERROR.getMsg() + "合利宝 支付方式," + payWayDO.getName() + ",默认费率必须配置");
            }
            PayEnums.SCENE_PAY_TYPE scenePayType = PayEnums.SCENE_PAY_TYPE.fromByType(payWayDO.getSubType());
            if (CommonEnum.YES_NO_TYPE.YES.getCode().equals(merchantFeeConfig.getStepFeeType())) {
                if (!CommonEnum.YES_NO_TYPE.YES.getCode().equals(merchantFeeConfig.getSeniorFeeType())) {
                    return CommonResult.error(MerchantResultCode.MERCHANT_ACCESS_PARAMS_ERROR.getCode(),
                            MerchantResultCode.MERCHANT_ACCESS_PARAMS_ERROR.getMsg() + "合利宝 阶梯费率启用与高级模式必填同时配置");
                }
            }

        }
        // switch (MerchantEnum.SETTLE_CYCLE.from(merchantAccessCard.getSettleCycle())) {
        //     case D0:
        //     case D1:
        //     case T1:
        //         break;
        //     default:
        //         return CommonResult.error(MerchantResultCode.MERCHANT_ACCESS_PARAMS_ERROR.getCode(),
        //                 MerchantResultCode.MERCHANT_ACCESS_PARAMS_ERROR.getMsg() + "合利宝结算周期不支持当前方式请检查");
        //
        // }

        return CommonResult.success();
    }

    /**
     * 企业商户进件-新增参数组装
     *
     * @param channelMerchantAccessContext
     * @return
     */
    public Map<String, Object> merchantApplyParams(ChannelMerchantAccessContext channelMerchantAccessContext, HeliPayConfig heliPayConfig) {
        Map<String, Object> params = new HashMap<>();
        MerchantAccessDto merchantAccessDto = channelMerchantAccessContext.getMerchantAccessDto();
        MerchantAccessBusinessDO merchantAccessBusiness = merchantAccessDto.getMerchantAccessBusiness();
        MerchantAccessInfoDO merchantAccessInfo = merchantAccessDto.getMerchantAccessInfo();
        MerchantAccessCertInfoDO merchantAccessLegal = merchantAccessDto.getMerchantAccessLegal();
        MerchantAccessCertInfoDO merchantAccessContact = merchantAccessDto.getMerchantAccessContact();
        MerchantAccessCardDO merchantAccessCard = merchantAccessDto.getMerchantAccessCard();
        List<MerchantAccessCertInfoDO> merchantAccessBenefitList = merchantAccessDto.getMerchantAccessBenefitList();
        List<ProviderChannelFeeConfigList> merchantFeeConfigList = channelMerchantAccessContext.getMerchantFeeConfigList();

        params.put("firstClassMerchantNo", heliPayConfig.getMerchantNo());

        // 判断新增还是修改，针对无商户又想修改进件信息,从PayChannelRequest 查询是否已请求过
        PayChannelRequestDO payChannelRequestDO = channelMerchantAccessContext.getBeforePayChannelRequestDO();
        if(payChannelRequestDO == null){
            params.put("orderNo", merchantAccessInfo.getRequestId());
        }else{
            String orderNo = IdUtils.getSnowflakeNextIdStr();
            params.put("orderNo",  orderNo);
            merchantAccessInfo.setRequestId(orderNo);
        }

        // 子商户类型 在mcc对于的Field1字段
        Map<String, PayChannelDictDO> result = channelMerchantAccessContext.getPayChannelDictList().stream()
                .collect(Collectors.toMap(PayChannelDictDO::getDictValue, Function.identity()));
        String field1 = result.get(merchantAccessBusiness.getMcc()).getField1();
        HeliPayMccDto heliPayMccDto = JsonUtils.parseObject(field1, HeliPayMccDto.class);
        params.put("merchantType", heliPayMccDto.getMerchantType());
        switch (MerchantEnum.MERCHANT_TYPE.from(merchantAccessInfo.getType())) {
            case PERSONAL:
                params.put("signName", "商户_" + merchantAccessLegal.getName());
                params.put("orgNum", merchantAccessLegal.getCertNo());
                params.put("specialSignName", true);
                params.put("microBizType", "MICRO_TYPE_MOBILE");
                params.put("businessLicense", merchantAccessLegal.getCertNo());
                params.put("merchantType", "PERSON");
                break;
            default:
                MerchantAccessLicenseDO merchantAccessLicense = merchantAccessDto.getMerchantAccessLicense();
                params.put("signName", merchantAccessLicense.getName());
                params.put("orgNum", merchantAccessLicense.getLicenseCode());
                params.put("businessLicense", merchantAccessLicense.getLicenseCode());
                params.put("businessDateStart", date(merchantAccessLicense.getLicenseBeginDate()));
                params.put("accountIdCard", merchantAccessLegal.getCertNo());
                params.put("postalAddress", merchantAccessLegal.getAddress());
                params.put("certType", licenseType(merchantAccessLicense.getLicenseType()));
                if (merchantAccessLicense.getName().contains("*")) {
                    params.put("specialSignName", true);
                }
                MerchantAccessCertInfoDO merchantAccessCertInfoDO;
                if (CollectionUtils.isEmpty(merchantAccessBenefitList)) {
                    merchantAccessCertInfoDO = merchantAccessLegal;
                }
                else {
                    merchantAccessCertInfoDO = merchantAccessBenefitList.get(0);
                }
                if (merchantAccessLegal.getCertNo().equals(merchantAccessCertInfoDO.getCertNo())) {
                    params.put("enterpriseOwner", true);
                }
                else {
                    params.put("enterpriseOwner", false);
                }
                params.put("benefLegalPersonIdType", certType(merchantAccessCertInfoDO.getCertType()));
                params.put("benefLegalPerson", merchantAccessCertInfoDO.getName());
                params.put("benefLegalPersonID", merchantAccessCertInfoDO.getCertNo());
                params.put("benefAddress", merchantAccessCertInfoDO.getAddress());
                params.put("benefIdCardStartDate", date(merchantAccessCertInfoDO.getCertBeginDate()));
                params.put("benefIdCardEndDate", date(merchantAccessCertInfoDO.getCertEndDate()));

        }
        params.put("showName", merchantAccessBusiness.getShortName());
        List<MerchantAccessScene> sceneList = merchantAccessBusiness.getSceneList();
        if (!CollectionUtils.isEmpty(sceneList)) {
            List<MerchantAccessScene> collect = sceneList.stream().filter(scene -> StringUtils.hasText(scene.getWebUrl())).collect(Collectors.toList());
            if (!CollectionUtils.isEmpty(sceneList)) {
                params.put("webSite", collect.get(0).getWebUrl());
            }
        }

        params.put("legalPerson", merchantAccessLegal.getName());

        params.put("legalPersonID", merchantAccessLegal.getCertNo());
        params.put("regionCode", merchantAccessBusiness.getAreaCode());
        params.put("address", merchantAccessBusiness.getAddress());
        params.put("linkman", merchantAccessContact.getName());
        params.put("linkPhone", merchantAccessContact.getPhone());
        params.put("email", merchantAccessContact.getOtherValue());
        params.put("servicePhone", merchantAccessContact.getPhone());
        params.put("bindMobile", merchantAccessContact.getPhone());

        params.put("bankCode", merchantAccessCard.getBranchCode());
        params.put("accountNo", merchantAccessCard.getCardNo());
        params.put("accountName", merchantAccessCard.getName());
        switch (MerchantEnum.CARD_TYPE.from(merchantAccessCard.getType())) {
            case CORPORATE_ACCOUNT:
                params.put("settleBankType", "TOPUBLIC");
                break;
            default:
                params.put("settleBankType", "TOPRIVATE");
        }

        ProviderChannelFeeConfigList settleCycleConf = getSettleCycle(merchantFeeConfigList);
        if (MerchantEnum.SETTLE_CYCLE.D0.getPayWayCode().equals(settleCycleConf.getPayWayDO().getCode())) {
            params.put("settlementMode", "AUTO");
            params.put("settlementPeriod", "D0");
        } else if (MerchantEnum.SETTLE_CYCLE.D1.getPayWayCode().equals(settleCycleConf.getPayWayDO().getCode())) {
            params.put("settlementMode", "AUTO");
            params.put("settlementPeriod", "D1");
        } else if (MerchantEnum.SETTLE_CYCLE.T1.getPayWayCode().equals(settleCycleConf.getPayWayDO().getCode())) {
            params.put("settlementMode", "AUTO");
            params.put("settlementPeriod", "T1");
        } else if (MerchantEnum.SETTLE_CYCLE.MANUAL.getPayWayCode().equals(settleCycleConf.getPayWayDO().getCode())) {
            params.put("settlementMode", "SELF");
            params.put("settlementPeriod", "D1");
        }

        params.put("merchantCategory", heliPayMccDto.getMerchantCategory());
        params.put("industryTypeCode", merchantAccessBusiness.getMcc());
        params.put("authorizationFlag", true);
        params.put("needPosFunction", false);
        params.put("idCardStartDate", date(merchantAccessLegal.getCertBeginDate()));
        params.put("idCardEndDate", date(merchantAccessLegal.getCertEndDate()));
        params.put("mcc", heliPayMccDto.getMcc());
        params.put("agreeProtocol", true);
        params.put("callbackUrl", channelMerchantAccessContext.getAsyncReturnUrl());
        params.put("settleMode", "MERCHANT");
        params.put("linkManId", merchantAccessContact.getCertNo());
        params.put("needAuthorize", true);
        params.put("idType", certType(merchantAccessLegal.getCertType()));
        if (CommonEnum.YES_NO_TYPE.YES.getCode().equals(merchantAccessInfo.getContactType())) {
            params.put("linkmanType", "LEGAL");
            params.put("idHolderType", "LEGAL");
        }
        else {
            params.put("linkmanType", "SUPER");
            params.put("idHolderType", "SUPER");
        }
        params.put("linkmanIdType", certType(merchantAccessContact.getCertType()));
        params.put("linkmanIdCardStartDate", date(merchantAccessContact.getCertBeginDate()));
        params.put("linkmanIdCardEndDate", date(merchantAccessContact.getCertEndDate()));
        params.put("legalPersonIdAddress", merchantAccessLegal.getAddress());
        return params;
    }

    private static ProviderChannelFeeConfigList getSettleCycle(List<ProviderChannelFeeConfigList> merchantFeeConfigList) {
        return merchantFeeConfigList.stream().filter(conf -> Objects.equals(conf.getPayWayDO().getType(), PayEnums.PAY_TYPE.FUNCTION.getCode())).findFirst().get();
    }

    /**
     * 企业商户进件-更新参数组装
     *
     * @param channelMerchantAccessContext
     * @return
     */
    public Map<String, Object> updateMerchantApplyParams(ChannelMerchantAccessContext channelMerchantAccessContext) {
        Map<String, Object> params = new HashMap<>();
        MerchantAccessDto merchantAccessDto = channelMerchantAccessContext.getMerchantAccessDto();
        MerchantAccessBusinessDO merchantAccessBusiness = merchantAccessDto.getMerchantAccessBusiness();
        MerchantAccessInfoDO merchantAccessInfo = merchantAccessDto.getMerchantAccessInfo();
        MerchantAccessCertInfoDO merchantAccessLegal = merchantAccessDto.getMerchantAccessLegal();
        MerchantAccessCertInfoDO merchantAccessContact = merchantAccessDto.getMerchantAccessContact();

        params.put("merchantNo", merchantAccessInfo.getSubMchId());
        params.put("merchantEntryAlterationType", "MERCHANT_INFO");
        params.put("updateShowName", merchantAccessBusiness.getShortName());
        params.put("updateLinkPhone", merchantAccessContact.getPhone());
        params.put("linkman", merchantAccessContact.getName());
        params.put("linkPhone", merchantAccessContact.getPhone());
        params.put("legalPersonID", merchantAccessLegal.getCertNo());
        params.put("idCardStartDate", date(merchantAccessLegal.getCertBeginDate()));
        params.put("idCardEndDate", date(merchantAccessLegal.getCertEndDate()));
        params.put("address", merchantAccessBusiness.getAddress());
        // 子商户类型 在mcc对于的Field1字段
        Map<String, PayChannelDictDO> result = channelMerchantAccessContext.getPayChannelDictList().stream()
                .collect(Collectors.toMap(PayChannelDictDO::getDictValue, Function.identity()));
        String field1 = result.get(merchantAccessBusiness.getMcc()).getField1();
        HeliPayMccDto heliPayMccDto = JsonUtils.parseObject(field1, HeliPayMccDto.class);
        params.put("merchantCategory", heliPayMccDto.getMerchantCategory());
        params.put("regionCode", merchantAccessBusiness.getAreaCode());
        switch (MerchantEnum.MERCHANT_TYPE.from(merchantAccessInfo.getType())) {
            case PERSONAL:
                break;
            default:
                MerchantAccessLicenseDO merchantAccessLicense = merchantAccessDto.getMerchantAccessLicense();
                params.put("businessLicense", merchantAccessLicense.getLicenseCode());
        }
        return params;
    }

    /**
     * 企业商户进件-新增参数组装
     *
     * @param merchantFeeConfig
     * @return
     */
    public Map<String, Object> merchantOpenAppPayProductParams(ProviderChannelFeeConfigList merchantFeeConfig) {
        List<String> onlineCardTypes = new ArrayList<>();
        onlineCardTypes.add("DEBIT");
        onlineCardTypes.add("CREDIT");
        Map<String, Object> params = new HashMap<>();
        params.put("productType", "APPPAY");
        PayWayDO payWayDO = merchantFeeConfig.getPayWayDO();
        PayEnums.SCENE_PAY_TYPE scenePayType = PayEnums.SCENE_PAY_TYPE.fromByType(payWayDO.getSubType());
        switch (scenePayType) {
            case H5:
            case WAP:
                params.put("payType", "WAP");
                break;
            case JSAPI:
                params.put("payType", "PUBLIC");
                break;
            case MINI_APP:
                params.put("payType", "APPLET");
                break;
            case QR:
                params.put("payType", "SWIPE");
                break;
            case SCAN:
                params.put("payType", "SCAN");
                break;
            default:
                return null;
        }
        PayEnums.PAY_TYPE payType = PayEnums.PAY_TYPE.fromByCode(payWayDO.getType());
        switch (payType) {
            case WECHAT:
                params.put("appPayType", "WXPAY");
                break;
            case ALIPAY:
                params.put("appPayType", "ALIPAY");
                break;
            case UNION_APY:
                params.put("appPayType", "UNIONPAY_ALL");
                break;
            case JDBT:
                params.put("appPayType", "JDPAY");
                break;
            default:
                return null;
        }
        DefaultFeeConfig defaultFeeConfigValue = merchantFeeConfig.getDefaultFeeConfigValue();
        switch (PayEnums.FEE_TYPE.from(defaultFeeConfigValue.getFeeType())) {
            case PERCENTAGE:
                params.put("value", defaultFeeConfigValue.getRateBase());
                break;
            case FIXED_VALUE:
                params.put("value", defaultFeeConfigValue.getFixedRate());
                break;
            default:
                params.put("value", defaultFeeConfigValue.getRateBase());
                params.put("minFee", defaultFeeConfigValue.getFixedRate());
        }

        if (CommonEnum.YES_NO_TYPE.YES.getCode().equals(merchantFeeConfig.getStepFeeType())) {
            List<StepFeeConfig> stepFeeConfigs = merchantFeeConfig.getStepFeeConfigs();
            params.put("appFeeMode", "RANGE");
            List<Map<String, Object>> feeRanges = new ArrayList<>();
            SeniorFeeConfig seniorFeeConfigValue = merchantFeeConfig.getSeniorFeeConfigValue();
            stepFeeConfigs.stream().forEach(stepFeeConfig -> {
                onlineCardTypes.stream().forEach(onlineCardType -> {
                    Map<String, Object> feeRange = new HashMap<>();
                    feeRange.put("start", stepFeeConfig.getStartAmt());
                    feeRange.put("end", stepFeeConfig.getEndAmt());
                    feeRange.put("fee", stepFeeConfig.getRateBase());
                    feeRange.put("minFee", seniorFeeConfigValue.getProviderMinFee());
                    feeRange.put("maxFee", seniorFeeConfigValue.getProviderMaxFee());
                    feeRange.put("calcType", "RATIO");
                    feeRange.put("onlineCardType", onlineCardType);
                    feeRanges.add(feeRange);
                });
            });
            params.put("feeRanges", feeRanges);
        }
        else {
            params.put("appFeeMode", "DEFAULT");
        }
        return params;
    }

    /**
     * 企业商户进件-新增参数组装
     *
     * @param merchantFeeConfig
     * @return
     */
    public Map<String, Object> merchantOpenSettleProductParams(MerchantAccessCardDO merchantAccessCard, ProviderChannelFeeConfigList merchantFeeConfig) {
        Map<String, Object> params = new HashMap<>();
        params.put("productType", "SETTLEMENT");
        if (MerchantEnum.SETTLE_CYCLE.D0.getPayWayCode().equals(merchantFeeConfig.getPayWayDO().getCode())) {
            params.put("settlementPeriod", "D0");
        } else {
            params.put("settlementPeriod", "T1");
        }
        switch (MerchantEnum.CARD_TYPE.from(merchantAccessCard.getType())) {
            case CORPORATE_ACCOUNT:
                params.put("settleBankType", "TOPUBLIC");
                break;
            default:
                params.put("settleBankType", "TOPRIVATE");
        }
        DefaultFeeConfig defaultFeeConfigValue = merchantFeeConfig.getDefaultFeeConfigValue();
        switch (PayEnums.FEE_TYPE.from(defaultFeeConfigValue.getFeeType())) {
            case PERCENTAGE:
                params.put("floating", defaultFeeConfigValue.getRateBase());
                params.put("calcType", "SINGLE");
                break;
            case FIXED_VALUE:
                params.put("floating", defaultFeeConfigValue.getFixedRate());
                params.put("calcType", "SINGLE");
                break;
            default:
                params.put("floating", defaultFeeConfigValue.getRateBase());
                params.put("calcType", "SINGLE");
        }
        return params;
    }
}
